package sys

import (
	"context"
	"fmt"
	"github.com/google/wire"
	"sieve_admin_server_userpass/app/dao"
	"sieve_admin_server_userpass/app/dao/menu"
	"sieve_admin_server_userpass/app/server"
	"sieve_admin_server_userpass/lib/log"
	"strings"
	"time"
)

var (
	MenuSet = wire.NewSet(wire.Struct(new(MenuSrv), "*"))
)

type MenuSrv struct {
	MenuRepo *dao.MenuRepo
}

var (
	initFinished bool
	sysMenu      []*server.BatchModTree
)

func (a *MenuSrv) QueryMenu(ctx context.Context, item map[string]interface{}) interface{} {

	var (
		level int
		menus []map[string]interface{}
	)

	if v, ok := item["level"]; ok {
		level = int(v.(uint64))
	}

	for _, v := range sysMenu {

		if level == 1 {
			menus = append(menus, map[string]interface{}{
				"name":  v.Name,
				"title": v.Title,
			})
		} else if level == 2 {
			for _, vv := range v.Children {
				menus = append(menus, map[string]interface{}{
					"name":  vv.Name,
					"title": vv.Title,
				})
			}
		} else {
			menus = append(menus, map[string]interface{}{
				"name":  v.Name,
				"title": v.Title,
			})
			for _, vv := range v.Children {
				menus = append(menus, map[string]interface{}{
					"name":  vv.Name,
					"title": vv.Title,
				})
			}
		}
	}

	return menus
}

func (a *MenuSrv) Create(ctx context.Context, item map[string]interface{}) interface{} {

	var (
		now = time.Now().Unix()
		def = map[string]interface{}{
			"create_at":  now,
			"updated_at": now,
		}
	)

	for k, v := range def {
		item[k] = v
	}

	return a.MenuRepo.Create(ctx, item)
}

func (a *MenuSrv) WaitMenuFinish(ctx context.Context) {

	for !initFinished {
		time.Sleep(time.Millisecond * 20)
	}
	return
}

func (a *MenuSrv) InitMenusWrap(ctx context.Context, nodes []*server.BatchModTree) interface{} {

	resp := a.InitMenus(ctx, nodes)
	if _, ok := resp.(error); ok {
		return resp
	}

	sysMenu = nodes
	initFinished = true
	return resp
}

func (a *MenuSrv) InitMenus(ctx context.Context, nodes []*server.BatchModTree) interface{} {

	var (
		existsIds   []string
		createItems []map[string]interface{}
	)
	for _, node := range nodes {
		var (
			tableName   = menu.Menu{}.TableName()
			module      = node.Name
			moduleName  = node.Title
			moduleOrder = node.Order
			moduleIcon  = node.Icon
		)

		for _, v := range node.Children {

			var (
				entity = menu.Menu{}
				now    = time.Now()
				item   = map[string]interface{}{
					"module":       module,
					"module_name":  moduleName,
					"item":         v.Name,
					"item_name":    v.Title,
					"module_order": moduleOrder,
					"item_order":   v.Order,
					"module_icon":  moduleIcon,
					"status":       v.DisableMenu,
					"item_icon":    v.Icon,
					"create_at":    now.Unix(),
					"updated_at":   now.Unix(),
				}
				checkDupSQL = fmt.Sprintf(`
		SELECT
			*
		FROM
		   %v 
		WHERE 
		    module = '%v' AND module_name = '%v' AND 
		    item = '%v' AND item_name = '%v' AND 
		    module_order = '%v' AND item_order = '%v' AND 
		    module_icon = '%v' AND item_icon = '%v'
`, tableName, module, moduleName, v.Name, v.Title, moduleOrder, v.Order, moduleIcon, v.Icon)
			)

			req, err := a.MenuRepo.QueryRaw(context.Background(), checkDupSQL, entity)
			if err != nil {
				log.Err("create daily recharge err:%+v", err)
				continue
			}

			if req == nil {
				// if err := a.MenuRepo.Create(context.Background(), item); err != nil {
				//	log.Err("create daily recharge err:%+v", err)
				// }
				createItems = append(createItems, item)
				continue
			}

			s := req[0].(*menu.Menu)
			existsIds = append(existsIds, fmt.Sprintf("%v", s.ID))
			if int(s.Status) == v.DisableMenu {
				continue
			}

			var (
				queries = map[string]interface{}{
					"id": s.ID,
				}
				updateKeys = map[string]interface{}{
					"status":     v.DisableMenu,
					"updated_at": now.Unix(),
				}
			)

			if err := a.MenuRepo.Update(context.Background(), updateKeys, queries); err != nil {
				log.Err("update menu err:%+v", err)
			}
		}
	}

	if len(existsIds) > 0 {
		var (
			deleteSQL = fmt.Sprintf(`
	DELETE FROM menu WHERE id NOT IN (%v)
`, strings.Join(existsIds, ","))
		)

		if _, err := a.MenuRepo.ExecRaw(context.Background(), deleteSQL); err != nil {
			log.Err("delete menus err:%+v", err)
			return err
		}
	}

	for _, item := range createItems {
		if err := a.MenuRepo.Create(context.Background(), item); err != nil {
			log.Err("create menu err:%+v", err)
		}
	}

	return nil
}

func (a *MenuSrv) GetAllMenus(ctx context.Context) ([]interface{}, error) {

	a.WaitMenuFinish(ctx)
	var (
		querySQL = fmt.Sprintf(`
						SELECT
						*
						FROM
							menu
						WHERE menu.deleted = 0 AND menu.status = 0
						ORDER BY module_order DESC,item_order DESC
		`)
	)

	return a.MenuRepo.QueryRaw(ctx, querySQL, menu.Menu{})
}

func (a *MenuSrv) GetMenus(ctx context.Context, id uint64, args ...interface{}) interface{} {

	type columnType struct {
		ID          uint64 `json:"id"`
		Module      string `json:"module"`       //  模块名称
		ModuleName  string `json:"module_name"`  //  模块名称
		Item        string `json:"item"`         //  菜单项
		ItemName    string `json:"item_name"`    //  菜单项
		ModuleOrder int    `json:"module_order"` //  模块排序
		ItemOrder   int    `json:"item_order"`   //  菜单排序
		ModuleIcon  string `json:"module_icon"`  //  模块图标
		ItemIcon    string `json:"item_icon"`    //  菜单图标
	}
	var (
		entity   = columnType{}
		querySQL = fmt.Sprintf(`
						SELECT
							menu.id,
							menu.module,
							menu.module_name,
							menu.item,
							menu.item_name,
							menu.module_order,
							menu.item_order,
							menu.module_icon,
							menu.item_icon
						FROM
						role_menu
						INNER JOIN menu ON role_menu.menu_id = menu.id AND menu.deleted = 0 AND menu.status = 0
						WHERE role_id IN (
							(
							SELECT
								role_id
							FROM user
							WHERE id = %v
							AND user.deleted = 0
							)
						)
						AND role_menu.deleted = 0 AND role_menu.status = 1
						ORDER BY menu.module_order DESC,menu.item_order DESC
		`, id)

		queryAllMenuSQL = fmt.Sprintf(`
						SELECT
							menu.id,
							menu.module,
							menu.module_name,
							menu.item,
							menu.item_name,
							menu.module_order,
							menu.item_order,
							menu.module_icon,
							menu.item_icon
						FROM
							menu
						WHERE menu.deleted = 0 AND menu.status = 0
						ORDER BY module_order DESC,item_order DESC
		`)
	)

	if id == uint64(adminRole.ID) {
		querySQL = queryAllMenuSQL
	}

	if len(args) == 1 {
		if v, ok := args[0].(bool); ok && v == true {
			querySQL = fmt.Sprintf(`
						SELECT
							menu.id,
							menu.module,
							menu.module_name,
							menu.item,
							menu.item_name,
							menu.module_order,
							menu.item_order,
							menu.module_icon,
							menu.item_icon
						FROM
						role_menu
						INNER JOIN menu ON role_menu.menu_id = menu.id AND menu.deleted = 0 AND menu.status = 0
						WHERE role_id = %v
						AND role_menu.deleted = 0 AND role_menu.status = 1
						ORDER BY menu.module_order DESC,menu.item_order DESC
		`, id)
		}
	}

	log.Info(fmt.Sprintf("get menus:%+v", querySQL))

	req, err := a.MenuRepo.QueryRaw(ctx, querySQL, entity)
	if err != nil {
		log.Err("get menus err:%+v", err)
		return err
	}

	if req == nil {
		return nil
	}

	var (
		menuMap     = make(map[string][]*server.ModTreeNode)
		menus       []*server.ModTreeNode
		moduleOrder []string
	)
	for _, v := range req {
		roleMenu := v.(*columnType)

		parent := roleMenu.Module + ":" + roleMenu.ModuleName
		if _, ok := menuMap[parent]; !ok {
			moduleOrder = append(moduleOrder, parent)
		}
		menuMap[parent] = append(menuMap[parent], &server.ModTreeNode{
			Mod: server.Mod{
				ID:    roleMenu.ID,
				Name:  roleMenu.Module + "/" + roleMenu.Item,
				Title: roleMenu.ItemName,
				Perms: []*server.Perm{},
			},
			Children: []*server.ModTreeNode{},
		})
	}

	for _, v := range moduleOrder {

		vv := menuMap[v]
		s := strings.Split(v, ":")
		menus = append(menus, &server.ModTreeNode{
			Mod: server.Mod{
				Name:  s[0],
				Title: s[1],
				Perms: []*server.Perm{},
			},
			Children: vv,
		})
	}

	return map[string]interface{}{
		"ResourceList": menus,
	}
}
